using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using DevExpress.XtraTabbedMdi;
using Moonlite.Services.CodeCompletion;
using Moonlite.ExtensionApi;
using Moonlite.ExtensionApi.ControlInterfaces;
using Moonlite.ExtensionApi.GlobalInterfaces;
using Moonlite.Platform.General;
using Moonlite.Platform.Project;
using Storm.TextEditor.Editor;
using Storm.TextEditor.Editor.CodeCompletion;
using Storm.TextEditor.Interacting.Keyboard;
using Storm.TextEditor.Languages;
using Storm.TextEditor.Parser.Objects;

namespace Moonlite.UI.Shell.Controls
{
    public partial class TextEditor : Form, ITextEditor
    {
        private readonly string[] JassNoCodeCompletionKeywords = new string[] {
            "return",
            "set",
            "call",
            "local",
            "if",
            "elseif",
            "exitwhen"
        };

        public static bool ParseOnSave { get; set; }
        public static string FontName { get; set; }
        public static int FontSize { get; set; }
        public static bool ConvertTabsToSpaces { get; set; }
        public static int TabSize { get; set; }
        public ICompletionDataProvider CompletionDataProvider
        {
            get { return InnerTextEditor.CodeCompletionWindow.CompletionDataProvider; }
        }

        public Storm.TextEditor.TextEditor InnerTextEditor
        {
            get { return innerTextEditor; }
        }

        public bool Saved
        {
            get
            {
                if (string.IsNullOrEmpty(InnerTextEditor.FileName) || !System.IO.File.Exists(InnerTextEditor.FileName)) return true;
                bool saved = true;
                try
                {
                    using (StreamReader reader = new StreamReader(InnerTextEditor.FileName))
                    {
                        if (reader.ReadToEnd() != InnerTextEditor.Text) saved = false;
                    }
                }
                catch { saved = false; }

                return saved;
            }
        }

        private Timer titleUpdateTimer;
        public IProjectFile File { get; set; }
        public XtraMdiTabPage ParentPage { get; set; }
        public GalaxyCompletionDataProvider GalaxyCompletionDataProvider { get; private set; }
        public JassCompletionDataProvider JassCompletionDataProvider { get; private set; }
        public int ParenthesisLevel { get; set; }
        public void Open(string filePath, IProjectFile file)
        {
            InnerTextEditor.Open(filePath);
            File = file;
            quickLinksControl.Hide();
            string extension = Path.GetExtension(filePath).Replace(".", "");
            InnerTextEditor.CodeCompletionWindow.CompletionDataProvider = null;
            if (extension == Global.GalaxyFileExtension || extension == "galaxy" || extension == "a")
            {
                InnerTextEditor.CodeCompletionWindow.CompletionDataProvider = GalaxyCompletionDataProvider;
                quickLinksControl.Show();
            }
            else if (extension == Global.JassFileExtension)
            {
                InnerTextEditor.CodeCompletionWindow.CompletionDataProvider = JassCompletionDataProvider;
                JassInheritanceManager.UpdateFile(InnerTextEditor.FileName);
            }

            InnerTextEditor.Document.ParseAll();
        }

        public void UpdateAllowJassCodeCompletionVisible(char inputKey)
        {
            if (inputKey == '.' && JassCompletionDataProvider.CheckAllowShowWhenStructTyped() == true)
            {
                JassCompletionDataProvider.AllowShow = true;
                return;
            }

            if (InnerTextEditor.Caret.CurrentWord != null && InnerTextEditor.Caret.CurrentWord.Segment != null &&
                (InnerTextEditor.Caret.CurrentWord.Segment.Block.Name.ToLower().Contains("string") == true ||
                InnerTextEditor.Caret.CurrentWord.Segment.Block.Name.ToLower().Contains("rawcode") == true))
            {
                JassCompletionDataProvider.AllowShow = false;
                return;
            }

            string rowText = innerTextEditor.Caret.CurrentRow.Text.Trim();
            if (string.IsNullOrEmpty(rowText) == true)
            {
                JassCompletionDataProvider.AllowShow = false;
                return;
            }

            foreach (string noCodeCompletionKeyword in JassNoCodeCompletionKeywords)
            {
                bool allowShow = (rowText.StartsWith(noCodeCompletionKeyword) == true || rowText.StartsWith(String.Format("debug {0}", noCodeCompletionKeyword)) == true) && inputKey != '.';
                if (Global.Instance.Project.Type == ProjectType.Jass)
                {
                    JassCompletionDataProvider.AllowShow = allowShow;
                    if (JassCompletionDataProvider.AllowShow != true)
                    {
                        continue;
                    }

                    break;
                }
            };
        }

        private string GetLastKeyword()
        {
            string keywordResult = "";
            int keywordArrayIndex = 0;
            int keywordStartIndex = 0;
            string[] keywords = InnerTextEditor.Caret.CurrentRow.Text.Split(' ');
            Array.ForEach(keywords, keyword =>
            {
                if (keywordStartIndex == InnerTextEditor.Selection.Bounds.FirstColumn || keywordStartIndex + keyword.Length == InnerTextEditor.Selection.Bounds.FirstColumn)
                {
                    if (keywordArrayIndex > 0)
                    {
                        keywordResult = keywords[keywordArrayIndex - 1];
                    }
                }

                keywordStartIndex += keyword.Length + 1;
                keywordArrayIndex++;
            });

            return keywordResult;
        }

        private void CalculateParenthesisLevel()
        {
            ParenthesisLevel = 0;
            Row startRow = InnerTextEditor.Caret.CurrentRow;
            int caretX = InnerTextEditor.Caret.Position.X;
            int caretY = InnerTextEditor.Caret.Position.Y;
            InnerTextEditor.CodeCompletionWindow.CommitChanges = false;
            InnerTextEditor.Caret.Position.X = 0;
            while (InnerTextEditor.Caret.CurrentRow == startRow && InnerTextEditor.Caret.Position.X < caretX)
            {
                if (InnerTextEditor.Caret.CurrentWord != null)
                {
                    if (InnerTextEditor.Caret.CurrentWord.Text == "(")
                    {
                        ParenthesisLevel++;
                    }

                    if (InnerTextEditor.Caret.CurrentWord.Text == ")")
                    {
                        ParenthesisLevel--;
                    }
                }

                InnerTextEditor.Caret.MoveRight(false);
            }

            InnerTextEditor.Caret.Position.X = caretX;
            InnerTextEditor.Caret.Position.Y = caretY;
            InnerTextEditor.CodeCompletionWindow.CommitChanges = true;
        }

        private string GetCurrentFunctionName()
        {
            Row startRow = InnerTextEditor.Caret.CurrentRow;
            int caretX = InnerTextEditor.Caret.Position.X;
            int caretY = InnerTextEditor.Caret.Position.Y;
            int parenthesisLevel = ParenthesisLevel;
            while (parenthesisLevel != ParenthesisLevel - 1 && InnerTextEditor.Caret.CurrentRow == startRow)
            {
                InnerTextEditor.Caret.MoveLeft(false);
                if (InnerTextEditor.Caret.CurrentWord == null)
                {
                    continue;
                }

                if (InnerTextEditor.Caret.CurrentWord.Text == "(")
                {
                    parenthesisLevel--;
                }

                if (InnerTextEditor.Caret.CurrentWord.Text == ")")
                {
                    parenthesisLevel++;
                }
            }

            Word currentWord = InnerTextEditor.Caret.CurrentWord;
            string function = currentWord.Text == "(" && currentWord.Row[currentWord.Index - 1] != null ? currentWord.Row[currentWord.Index - 1].Text : currentWord.Text;
            InnerTextEditor.Caret.Position.X = caretX;
            InnerTextEditor.Caret.Position.Y = caretY;
            return function;
        }

        public void UpdateFileName()
        {
            if (ParentPage == null) return;
            ParentPage.Text = Saved ? Path.GetFileName(InnerTextEditor.FileName) : Path.GetFileName(InnerTextEditor.FileName) + "*";
        }

        private void OnTextEditorKeyUp(object sender, KeyEventArgs e)
        {
            TextEditorEvents.OnKeyUp(e);
            if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
            {
                UpdateAllowJassCodeCompletionVisible((char)Keys.Enter);
            }
            if (e.KeyCode == Keys.Oemcomma && e.Control && !e.Shift && !e.Alt)
            {
                OnNavigateToShortcutPressed();
            }
            if (e.KeyCode == Keys.W && e.Control && !e.Shift && !e.Alt)
            {
                Close();
            }
            if (e.KeyCode == Keys.T && e.Control && e.Shift && !e.Alt)
            {
                quickLinksControl.ShowTypesDropDown();
            }
            if (e.KeyCode == Keys.M && e.Control && e.Shift && !e.Alt)
            {
                quickLinksControl.ShowMembersDropDown();
            }
        }

        private void OnTextEditorKeyPress(object sender, KeyPressEventArgs e)
        {
            typed = true;
            DynamicSyntaxCheckingScheduler.Enabled = false;
            testTypingTimer.Start();
            TextEditorEvents.OnKeyPress(e);
            if (Global.Instance.Project == null) return;
            if (Global.Instance.Project.Type == ProjectType.Jass && Path.GetExtension(InnerTextEditor.FileName) == "." + Global.JassFileExtension)
            {
                if (e.KeyChar == '=')
                {
                    JassCompletionDataProvider.ShouldUpdate = true;
                }

                string identifier = JassCompletionDataProvider.GetIdentifierName(this);
                if (e.KeyChar == '.' && (identifier == "" || identifier == "this"))
                {
                    JassCompletionDataProvider.DotTyped();
                }

                if (e.KeyChar == '(' || e.KeyChar == ')' || e.KeyChar == ',')
                {
                    CalculateParenthesisLevel();
                    if (ParenthesisLevel > 0)
                    {
                        string function = GetCurrentFunctionName();
                        JassCodeObject jassCodeObject = null;
                        GalaxyCodeObject galaxyCodeObject = null;
                        foreach (object item in ((IFunctionExplorer)Global.Instance.GetService(typeof(IFunctionExplorer))).Items)
                        {
                            if (Global.Instance.Project.Type == ProjectType.Jass)
                            {
                                JassCodeObject codeObject = (JassCodeObject)item;
                                if (codeObject.Name == function)
                                {
                                    jassCodeObject = codeObject;
                                    break;
                                }
                            }
                            else
                            {
                                GalaxyCodeObject codeObject = (GalaxyCodeObject)item;
                                if (codeObject.Name == function)
                                {
                                    galaxyCodeObject = codeObject;
                                    break;
                                }
                            }
                        }

                        if (jassCodeObject == null && galaxyCodeObject == null)
                        {
                            return;
                        }

                        ((ICodeExplorer)Global.Instance.GetService(typeof(ICodeExplorer))).SetCode(Global.Instance.Project.Type == ProjectType.Jass ? jassCodeObject.Code : galaxyCodeObject.Code);
                    }
                }

                JassCompletionDataProvider.UpdateInput();
                JassCompletionDataProvider.AllowShow = true;
                string lastKeyword = GetLastKeyword();
                if (JassObjectHelper.IsValidTypeName(lastKeyword) == true)
                {
                    JassCompletionDataProvider.AllowShow = false;
                    InnerTextEditor.CodeCompletionWindow.Hide();
                }
                else if (Global.Instance.Project.Type == ProjectType.Jass)
                {
                    UpdateAllowJassCodeCompletionVisible(e.KeyChar);
                    if (JassCompletionDataProvider.AllowShow == false)
                    {
                        InnerTextEditor.CodeCompletionWindow.Hide();
                    }
                }
            }
            else if (Global.Instance.Project.Type == ProjectType.Galaxy && Path.GetExtension(InnerTextEditor.FileName) == "." + Global.GalaxyFileExtension)
            {
                if (e.KeyChar == '.')
                {
                    GalaxyCompletionDataProvider.DotTyped();
                }
            }
        }

        private void OnTextEditorMouseDown(object sender, MouseEventArgs e)
        {
            TextEditorEvents.OnMouseDown(e);
            if (Global.Instance.Project != null && Global.Instance.Project.Type == ProjectType.Jass && Path.GetExtension(InnerTextEditor.FileName) == "." + Global.JassFileExtension)
            {
                UpdateAllowJassCodeCompletionVisible((char)Keys.Enter);
                JassCompletionDataProvider.FirstUpdate = true;
            }
        }

        private void OnTimerTick(object sender, EventArgs e)
        {
            if (Global.Instance.Project == null)
            {
                return;
            }

            if (DesignMode)
            {
                return;
            }

            if (Global.Instance.Project.Type == ProjectType.Jass && Path.GetExtension(InnerTextEditor.FileName) == "." + Global.JassFileExtension)
            {
                JassInheritanceManager.UpdateFile(InnerTextEditor.FileName);
            }

            if (InnerTextEditor.Text == "" || InnerTextEditor.CodeCompletionWindow.Visible != false || InnerTextEditor.Document.ParseQueue.Count != 0)
            {
                return;
            }

            string extension = Path.GetExtension(InnerTextEditor.FileName).Replace(".", "");
            if (extension == Global.JassFileExtension)
            {
                JassCompletionDataProvider.Update();
            }
        }

        private void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            if (ParentPage != null && Global.Instance.CloseFile(this, true, false) == false)
            {
                e.Cancel = true;
                return;
            }

            try
            {
                if (e.Cancel == false)
                {
                    Global.Instance.RemoveTextEditor(this);
                    Global.Instance.UpdateToolbars(Global.Instance.IsCurrentTabPageTextEditor(), Global.Instance.Project != null);
                }
            }
            catch (Exception exception)
            {
                Logger.Log += exception.Message + Environment.NewLine;
            }
        }

        private bool typed = true;
        private Timer testTypingTimer = new Timer();
        private void OnTextEditorTextChanged(object sender, EventArgs e)
        {
            UpdateFileName();
            testTypingTimer.Start();
        }

        private void OnTitleUpdateTimerTick(object sender, EventArgs e)
        {
            UpdateFileName();
        }

        private void OnGalaxyCompletionDataProviderUpdated(object sender, EventArgs e)
        {
            if (Global.Instance.Project == null) return;
            AndromedaSource currentSource = GalaxyObjectHelper.GetSourceFromFileName(InnerTextEditor.FileName, GalaxyCompletionDataProvider.LastStructure);
            if (currentSource != null)
            {
                quickLinksControl.UpdateSource(currentSource);
            }
        }

        private void OnNavigateToShortcutPressed()
        {
            if (Global.Instance == null || Global.Instance.Project == null) return;
            if (Global.Instance.Project.Type == ProjectType.Galaxy)
            {
                NavigateTo navigator = new NavigateTo();
                navigator.Structure = GalaxyCompletionDataProvider.LastStructure;
                navigator.ShowDialog();
            }
        }

        private void OnTextEditorClick(object sender, EventArgs e)
        {
            quickLinksControl.UpdateListsToMatchCurrentObject();
        }

        private void innerTextEditor_DragDrop(object sender, DragEventArgs e)
        {
            TextEditorEvents.OnDragDrop(e);
        }
        private void innerTextEditor_DragEnter(object sender, DragEventArgs e)
        {
            TextEditorEvents.OnDragEnter(e);
        }
        private void innerTextEditor_DragLeave(object sender, EventArgs e)
        {
            TextEditorEvents.OnDragLeave(e);
        }
        private void innerTextEditor_DragOver(object sender, DragEventArgs e)
        {
            TextEditorEvents.OnDropOver(e);
        }
        private void innerTextEditor_FileNameChanged(object sender, EventArgs e)
        {
            TextEditorEvents.OnFileNameChanged(e);
        }
        private void innerTextEditor_FileSavedChanged(object sender, EventArgs e)
        {
            TextEditorEvents.OnFileSavedChanged(e);
        }
        private void innerTextEditor_CaretChange(object sender, EventArgs e)
        {
            TextEditorEvents.OnCaretChanged(e);
        }
        private void innerTextEditor_ClipboardUpdated(object sender, CopyEventArgs e)
        {
            TextEditorEvents.OnClipboardUpdated(e);
        }
        private void innerTextEditor_KeyDown(object sender, KeyEventArgs e)
        {
            TextEditorEvents.OnKeyDown(e);
        }
        private void innerTextEditor_MouseClick(object sender, MouseEventArgs e)
        {
            TextEditorEvents.OnMouseClick(e);
        }
        private void innerTextEditor_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            TextEditorEvents.OnMouseDoubleClick(e);
        }
        private void innerTextEditor_MouseEnter(object sender, EventArgs e)
        {
            TextEditorEvents.OnMouseEnter(e);
        }
        private void innerTextEditor_MouseHover(object sender, EventArgs e)
        {
            TextEditorEvents.OnMouseHover(e);
        }
        private void innerTextEditor_MouseLeave(object sender, EventArgs e)
        {
            TextEditorEvents.OnMouseLeave(e);
        }
        private void innerTextEditor_MouseMove(object sender, MouseEventArgs e)
        {
            TextEditorEvents.OnMouseMove(e);
        }
        private void innerTextEditor_MouseUp(object sender, MouseEventArgs e)
        {
            TextEditorEvents.OnMouseUp(e);
        }
        private void innerTextEditor_RenderRow(object sender, RowPaintEventArgs e)
        {
            TextEditorEvents.OnRenderRow(e);
        }
        private void innerTextEditor_RowClick(object sender, RowMouseEventArgs e)
        {
            TextEditorEvents.OnRowClick(e);
        }
        private void innerTextEditor_RowDoubleClick(object sender, RowMouseEventArgs e)
        {
            TextEditorEvents.OnRowDoubleClick(e);
        }
        private void innerTextEditor_RowMouseDown(object sender, RowMouseEventArgs e)
        {
            TextEditorEvents.OnRowMouseDown(e);
        }
        private void innerTextEditor_RowMouseMove(object sender, RowMouseEventArgs e)
        {
            TextEditorEvents.OnRowMouseMove(e);
        }
        private void innerTextEditor_RowMouseUp(object sender, RowMouseEventArgs e)
        {
            TextEditorEvents.OnRowMouseUp(e);
        }
        private void innerTextEditor_SelectionChange(object sender, EventArgs e)
        {
            TextEditorEvents.OnSelectionChanged(e);
        }
        private void innerTextEditor_WordMouseDown(object sender, ref WordMouseEventArgs e)
        {
            TextEditorEvents.OnWordMouseDown(e);
        }
        private void innerTextEditor_WordMouseHover(object sender, ref WordMouseEventArgs e)
        {
            TextEditorEvents.OnWordMouseHover(e);
        }

        public TextEditor()
        {
            InitializeComponent();
            try { Global.Instance.RegisterService(this, GetType().GetInterface("ITextEditor")); }
            catch { }
            innerTextEditor.FontName = FontName;
            innerTextEditor.FontSize = FontSize;
            innerTextEditor.KeepTabs = !ConvertTabsToSpaces;
            innerTextEditor.TabSpaces = TabSize;
            quickLinksControl.TextEditor = this;
            GalaxyCompletionDataProvider = new GalaxyCompletionDataProvider();
            GalaxyCompletionDataProvider.Updated += OnGalaxyCompletionDataProviderUpdated;
            JassCompletionDataProvider = new JassCompletionDataProvider();
            InnerTextEditor.ParseOnSave = ParseOnSave;
            titleUpdateTimer = new Timer();
            titleUpdateTimer.Interval = 100;
            titleUpdateTimer.Tick += OnTitleUpdateTimerTick;
            titleUpdateTimer.Start();
            DynamicSyntaxCheckingScheduler.CheckTimeElapsed += OnTimerTick;
            testTypingTimer.Interval = 200;
            testTypingTimer.Tick += (object s, EventArgs ee) =>
            {
                if (!typed)
                {
                    DynamicSyntaxCheckingScheduler.Enabled = true;
                }
                typed = false;
            };
        }
    }
}
